Respect Language & Input preferences under Android

* doc/emacs/android.texi (Android Environment):

* doc/emacs/cmdargs.texi (General Variables): Mention the manner
in which the default language environment is selected on
Android.

* lisp/startup.el (normal-top-level): If android and
initial-window-system, call android-locale-for-system-language
for the default locale name.

* lisp/term/android-win.el (android-locale-for-system-language):
New function.

* src/androidfns.c (syms_of_androidfns_for_pdumper): New
function.
(syms_of_androidfns) <Vandroid_os_language>: New variable.
Call syms_of_androidfns_for_pdumper both now and after
loading the dump image.
This commit is contained in:
Po Lu 2023-12-14 13:24:42 +08:00
parent 33aa46fe94
commit de25aaa11a
5 changed files with 296 additions and 9 deletions

View file

@ -409,15 +409,23 @@ Startup}) connect the Android system to another computer, and run:
$ adb shell "settings put global settings_enable_monitor_phantom_procs false"
@end example
@cindex system language settings, Android
The ``Languages & Input'' preferences which apply to the operating
system do not influence the C locale set for programs, but is taken
into account by Emacs during startup: a locale name is generated from
the selected language and regional variant and a language environment
(@pxref{Language Environment}) is selected on that basis, which does
not overwrite @code{LANG} or other locale-related environment
variables. The coding system for language environments set in this
fashion is @code{utf-8-unix} without exception.
@cindex C locale settings, Android
Emacs does not respect the locale configured for user applications
in the system, for the selection of locales available there does not
match that supplied by the C library. When Emacs starts on Android
5.0 or newer, the @code{LANG} environment variable is set to
@code{en_US.utf8}, which induces subprocesses linked against the
Android C library to print output sensibly. Earlier versions of
Android do not implement locales at all, on account of which the
variable is set to @code{C} instead.
Instead, the @code{LANG} environment variable (@pxref{General
Variables}) is set to @code{en_US.utf8} when Emacs starts on Android
5.0 or newer, which induces subprocesses linked against the Android C
library to print output sensibly. Earlier versions of Android do not
implement locales at all, on account of which the variable is set to
@code{C} instead.
@cindex running emacs in the background, android
@cindex emacs killed, android

View file

@ -640,6 +640,11 @@ set this in the ``Regional Settings'' Control Panel on some versions
of MS-Windows, and in the ``Language and Region'' System Preference on
macOS.
When running a GUI session on Android, @env{LANG} is set to a fixed
value, but the language and locale environment is derived from the
system's ``Languages & Input'' preferences. @xref{Android
Environment}.
The value of the @env{LC_CTYPE} category is
matched against entries in @code{locale-language-names},
@code{locale-charset-language-names}, and

View file

@ -641,7 +641,23 @@ It is the default value of the variable `top-level'."
(setq eol-mnemonic-dos "(DOS)"
eol-mnemonic-mac "(Mac)")))
(set-locale-environment nil)
(if (and (featurep 'android)
(eq system-type 'android)
initial-window-system)
;; If Android windowing is enabled, derive a proper locale
;; from the system's language preferences. On Android, LANG
;; and LC_* must be set to one of the two locales the C
;; library supports, but, by contrast with other systems, the
;; C library locale does not reflect the configured system
;; language.
;;
;; For this reason, the locale from which Emacs derives a
;; default language environment is computed from such
;; preferences, rather than environment variables that the C
;; library refers to.
(set-locale-environment
(funcall 'android-locale-for-system-language))
(set-locale-environment nil))
;; Decode all default-directory's (probably, only *scratch* exists
;; at this point). default-directory of *scratch* is the basis
;; for many other file-name variables and directory lists, so it

View file

@ -424,6 +424,61 @@ denied. ")
'follow-link t)
(newline))))
;;; Locale preferences.
(defvar android-os-language)
(defun android-locale-for-system-language ()
"Return a locale representing the system language.
This locale reflects the system's language preferences in its
language name and country variant fields, and always specifies
the UTF-8 coding system."
;; android-os-language is a list comprising four elements LANGUAGE,
;; COUNTRY, SCRIPT, and VARIANT.
;;
;; LANGUAGE and COUNTRY are ISO language and country codes identical
;; to those stored within POSIX locales.
;;
;; SCRIPT is an ISO 15924 script tag, representing the script used
;; if available, or if required to disambiguate between distinct
;; writing systems for the same combination of language and country.
;;
;; VARIANT is an arbitrary string representing the variant of the
;; LANGUAGE or SCRIPT represented.
;;
;; Each of these fields might be empty, but the locale is invalid if
;; LANGUAGE is empty, which if true "en_US.UTF-8" is returned as a
;; placeholder.
(let ((language (or (nth 0 android-os-language) ""))
(country (or (nth 1 android-os-language) ""))
(script (or (nth 2 android-os-language) ""))
(variant (or (nth 3 android-os-language) ""))
locale-base locale-modifier)
(if (string-empty-p language)
(setq locale-base "en_US.UTF-8")
(if (string-empty-p country)
(setq locale-base (concat language ".UTF-8"))
(setq locale-base (concat language "_" country
".UTF-8"))))
;; No straightforward relation between Java script and variant
;; combinations exist: Java permits both a script and a variant to
;; be supplied at once, whereas POSIX's closest analog "modifiers"
;; permit only either an alternative script or a variant to be
;; supplied.
;;
;; Emacs disregards variants besides "EURO" and scripts besides
;; "Cyrl", for these two never coexist in existing locales, and
;; their POSIX equivalents are the sole modifiers recognized by
;; Emacs.
(if (string-equal script "Cyrl")
(setq locale-modifier "@cyrillic")
(if (string-equal variant "EURO")
(setq locale-modifier "@euro")
(setq locale-modifier "")))
;; Return the concatenation of both these values.
(concat locale-base locale-modifier)))
(provide 'android-win)
;; android-win.el ends here.

View file

@ -27,6 +27,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "keyboard.h"
#include "buffer.h"
#include "androidgui.h"
#include "pdumper.h"
#ifndef ANDROID_STUBIFY
@ -3170,6 +3171,186 @@ android_set_preeditarea (struct window *w, int x, int y)
#ifndef ANDROID_STUBIFY
static void
syms_of_androidfns_for_pdumper (void)
{
jclass locale;
jmethodID method;
jobject object;
jstring string;
Lisp_Object language, country, script, variant;
const char *data;
/* Find the Locale class. */
locale = (*android_java_env)->FindClass (android_java_env,
"java/util/Locale");
if (!locale)
emacs_abort ();
/* And the method from which the default locale can be
extracted. */
method = (*android_java_env)->GetStaticMethodID (android_java_env,
locale,
"getDefault",
"()Ljava/util/Locale;");
if (!method)
emacs_abort ();
/* Retrieve the default locale. */
object = (*android_java_env)->CallStaticObjectMethod (android_java_env,
locale, method);
android_exception_check_1 (locale);
if (!object)
emacs_abort ();
/* Retrieve its language field. Each of these methods is liable to
return the empty string, though if language is empty, the locale
is malformed. */
method = (*android_java_env)->GetMethodID (android_java_env, locale,
"getLanguage",
"()Ljava/lang/String;");
if (!method)
emacs_abort ();
string = (*android_java_env)->CallObjectMethod (android_java_env, object,
method);
android_exception_check_2 (object, locale);
if (!string)
language = empty_unibyte_string;
else
{
data = (*android_java_env)->GetStringUTFChars (android_java_env,
string, NULL);
android_exception_check_3 (object, locale, string);
if (!data)
language = empty_unibyte_string;
else
{
language = build_unibyte_string (data);
(*android_java_env)->ReleaseStringUTFChars (android_java_env,
string, data);
}
}
/* Delete the reference to this string. */
ANDROID_DELETE_LOCAL_REF (string);
/* Proceed to retrieve the country code. */
method = (*android_java_env)->GetMethodID (android_java_env, locale,
"getCountry",
"()Ljava/lang/String;");
if (!method)
emacs_abort ();
string = (*android_java_env)->CallObjectMethod (android_java_env, object,
method);
android_exception_check_2 (object, locale);
if (!string)
country = empty_unibyte_string;
else
{
data = (*android_java_env)->GetStringUTFChars (android_java_env,
string, NULL);
android_exception_check_3 (object, locale, string);
if (!data)
country = empty_unibyte_string;
else
{
country = build_unibyte_string (data);
(*android_java_env)->ReleaseStringUTFChars (android_java_env,
string, data);
}
}
ANDROID_DELETE_LOCAL_REF (string);
/* Proceed to retrieve the script. */
method = (*android_java_env)->GetMethodID (android_java_env, locale,
"getScript",
"()Ljava/lang/String;");
if (!method)
emacs_abort ();
string = (*android_java_env)->CallObjectMethod (android_java_env, object,
method);
android_exception_check_2 (object, locale);
if (!string)
script = empty_unibyte_string;
else
{
data = (*android_java_env)->GetStringUTFChars (android_java_env,
string, NULL);
android_exception_check_3 (object, locale, string);
if (!data)
script = empty_unibyte_string;
else
{
script = build_unibyte_string (data);
(*android_java_env)->ReleaseStringUTFChars (android_java_env,
string, data);
}
}
ANDROID_DELETE_LOCAL_REF (string);
/* And variant. */
method = (*android_java_env)->GetMethodID (android_java_env, locale,
"getVariant",
"()Ljava/lang/String;");
if (!method)
emacs_abort ();
string = (*android_java_env)->CallObjectMethod (android_java_env, object,
method);
android_exception_check_2 (object, locale);
if (!string)
variant = empty_unibyte_string;
else
{
data = (*android_java_env)->GetStringUTFChars (android_java_env,
string, NULL);
android_exception_check_3 (object, locale, string);
if (!data)
variant = empty_unibyte_string;
else
{
variant = build_unibyte_string (data);
(*android_java_env)->ReleaseStringUTFChars (android_java_env,
string, data);
}
}
/* Delete the reference to this string. */
ANDROID_DELETE_LOCAL_REF (string);
/* And other remaining local references. */
ANDROID_DELETE_LOCAL_REF (object);
ANDROID_DELETE_LOCAL_REF (locale);
/* Set Vandroid_os_language. */
Vandroid_os_language = list4 (language, country, script, variant);
}
#endif /* ANDROID_STUBIFY */
void
syms_of_androidfns (void)
{
@ -3313,6 +3494,26 @@ element that is activated for a given number of milliseconds upon the
bell being rung. */);
android_keyboard_bell_duration = 50;
DEFVAR_LISP ("android-os-language", Vandroid_os_language,
doc: /* List representing the system language configured.
This list incorporates four elements LANGUAGE, COUNTRY, SCRIPT
and VARIANT, of which:
LANGUAGE and COUNTRY are ISO language and country codes identical to
those stored within POSIX locales.
SCRIPT is an ISO 15924 script tag, representing the script used
if available, or if required to disambiguate between distinct
writing systems for the same combination of language and country.
VARIANT is an arbitrary string representing the variant of the
LANGUAGE or SCRIPT represented.
Each of these fields might be empty or nil, but the locale is invalid
if LANGUAGE is empty. Users of this variable should consider the
language US English in this scenario. */);
Vandroid_os_language = Qnil;
/* Functions defined. */
defsubr (&Sx_create_frame);
defsubr (&Sxw_color_defined_p);
@ -3363,5 +3564,7 @@ bell being rung. */);
staticpro (&tip_dx);
tip_dy = Qnil;
staticpro (&tip_dy);
pdumper_do_now_and_after_load (syms_of_androidfns_for_pdumper);
#endif /* !ANDROID_STUBIFY */
}