Skip to content

Commit f5a4047

Browse files
authored
Merge pull request #19 from hoc081098/copilot/update-readme-file-content
Update README.md to reflect latest codebase features and implementation
2 parents cc8fe17 + 23b61a4 commit f5a4047

File tree

1 file changed

+131
-33
lines changed

1 file changed

+131
-33
lines changed

README.md

Lines changed: 131 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@ This project showcases **best practices** for implementing localization in moder
2121
- **🔀 Follow System Option**: Seamlessly follow device locale settings
2222
- **🎯 Per-App Language Settings**: Uses AndroidX AppCompat's `setApplicationLocales()` API (API 27+)
2323
- **📊 Live Locale Information**: Real-time display of current locale details
24+
- **🌐 Accept-Language Header Demo**: HTTP request demonstration with automatic locale-aware Accept-Language headers
2425

2526
## Supported Languages
2627

27-
- **English** (en, en-US)
28-
- **Vietnamese** (vi, vi-VN)
28+
- **English** (en)
29+
- **Vietnamese** (vi-VN)
2930

3031
The app dynamically displays available languages from `BuildConfig` and highlights the currently selected one.
3132

@@ -56,28 +57,38 @@ Run the app and tap on a language to see instant language switching with locale-
5657
- **AndroidX AppCompat** - Per-app language preferences API
5758
- **AndroidX Lifecycle** - Lifecycle-aware components
5859
- **Java Time API** - Modern date/time handling with ICU patterns
60+
- **Retrofit** - Type-safe HTTP client for network requests
61+
- **Moshi** - Modern JSON library for Kotlin
62+
- **OkHttp** - HTTP client with interceptor support
5963

6064
## Project Structure
6165

6266
```
6367
app/src/main/
6468
├── java/com/hoc081098/jetpackcomposelocalization/
6569
│ ├── MainActivity.kt # Main activity with language switching
70+
│ ├── DemoAcceptLanguageHeader.kt # Accept-Language header demo
71+
│ ├── MyApplication.kt # Application class for initialization
72+
│ ├── data/
73+
│ │ ├── AcceptedLanguageInterceptor.kt # OkHttp interceptor for Accept-Language
74+
│ │ ├── ApiService.kt # Retrofit API interface
75+
│ │ └── NetworkServiceLocator.kt # Network service configuration
6676
│ └── ui/
6777
│ ├── locale/
68-
│ │ └── currentLocale.kt # Composable to get current locale
78+
│ │ ├── AppLocaleManager.kt # Locale management and state
79+
│ │ └── currentLocale.kt # Composable to get current locale
6980
│ ├── text/
70-
│ │ └── DateTimeFormatterCache.kt # 🔥 Intelligent formatter caching
81+
│ │ └── DateTimeFormatterCache.kt # 🔥 Intelligent formatter caching
7182
│ ├── time/
72-
│ │ └── Instant.kt # Extension functions for time formatting
83+
│ │ └── Instant.kt # Extension functions for time formatting
7384
│ └── theme/
74-
│ ├── Color.kt # Color definitions
75-
│ ├── Theme.kt # Material Theme configuration
76-
│ └── Type.kt # Typography definitions
85+
│ ├── Color.kt # Color definitions
86+
│ ├── Theme.kt # Material Theme configuration
87+
│ └── Type.kt # Typography definitions
7788
└── res/
78-
├── values/ # Default resources (English)
89+
├── values/ # Default resources (English)
7990
│ └── strings.xml
80-
└── values-vi/ # Vietnamese resources
91+
└── values-vi/ # Vietnamese resources
8192
└── strings.xml
8293
```
8394

@@ -107,16 +118,21 @@ cd Jetpack-Compose-Localization
107118

108119
### Language Switching
109120

110-
The app uses AndroidX AppCompat's per-app language preferences API with support for "Follow System" mode:
121+
The app uses `AppLocaleManager` with AndroidX AppCompat's per-app language preferences API with support for "Follow System" mode:
111122

112123
```kotlin
113-
private fun changeLanguage(language: String) {
114-
if (language == FOLLOW_SYSTEM) {
115-
// Set empty locale list to follow system
116-
AppCompatDelegate.setApplicationLocales(LocaleListCompat.getEmptyLocaleList())
117-
} else {
118-
val locale = Locale(language)
119-
AppCompatDelegate.setApplicationLocales(LocaleListCompat.create(locale))
124+
@Stable
125+
class AppLocaleManager {
126+
fun changeLanguage(locale: AppLocaleState.AppLocale) {
127+
val target = when (locale) {
128+
AppLocaleState.AppLocale.FollowSystem ->
129+
// Set empty locale list to follow system
130+
LocaleListCompat.getEmptyLocaleList()
131+
132+
is AppLocaleState.AppLocale.Language ->
133+
LocaleListCompat.create(locale.locale)
134+
}
135+
AppCompatDelegate.setApplicationLocales(target)
120136
}
121137
}
122138
```
@@ -219,15 +235,27 @@ val zonedDateTime = instant.toZonedDateTime(ZoneId.systemDefault())
219235
The build configuration defines supported locales:
220236

221237
```kotlin
222-
val SUPPORTED_LOCALES = setOf(
223-
"en",
224-
"en-rUS",
225-
"vi",
226-
"vi-rVN",
227-
)
238+
object Locales {
239+
val localeFilters = listOf(
240+
"en",
241+
"vi-rVN",
242+
)
243+
244+
val supportedLocales: String =
245+
localeFilters.joinToString(
246+
separator = ",",
247+
prefix = "\"",
248+
postfix = "\""
249+
) {
250+
it.replace(
251+
oldValue = "-r",
252+
newValue = "-"
253+
)
254+
}
255+
}
228256
```
229257

230-
These are automatically filtered during the build process and available via `BuildConfig.SUPPORTED_LANGUAGE_CODES`.
258+
These are automatically exposed via `BuildConfig.SUPPORTED_LOCALES` (comma-separated string: `"en,vi-VN"`).
231259

232260
## Advanced Features
233261

@@ -265,23 +293,93 @@ The app provides a "Follow System" option that:
265293
Supported locales are automatically exposed via BuildConfig:
266294

267295
```kotlin
268-
val SUPPORTED_LOCALES = setOf("en", "en-rUS", "vi", "vi-rVN")
269-
// Available at runtime as: BuildConfig.SUPPORTED_LANGUAGE_CODES
296+
object Locales {
297+
val localeFilters = listOf("en", "vi-rVN")
298+
val supportedLocales: String = localeFilters.joinToString(",", "\"", "\"") {
299+
it.replace("-r", "-")
300+
}
301+
}
302+
// Available at runtime as: BuildConfig.SUPPORTED_LOCALES = "en,vi-VN"
270303
```
271304

272-
This enables dynamic UI generation without hardcoding language options.
305+
The `AppLocaleManager` parses this string to dynamically generate language options without hardcoding.
306+
307+
### Accept-Language Header Demo
308+
309+
The app includes a practical demonstration of sending locale-aware HTTP requests with the Accept-Language header:
310+
311+
**Key Components:**
312+
313+
1. **AcceptedLanguageInterceptor** - OkHttp interceptor that automatically adds Accept-Language header:
314+
```kotlin
315+
internal class AcceptedLanguageInterceptor(
316+
private val localeProvider: LocaleProvider,
317+
) : Interceptor {
318+
override fun intercept(chain: Interceptor.Chain): Response {
319+
val locales = localeProvider.provide()
320+
val request = chain.request()
321+
.newBuilder()
322+
.addHeader("Accept-Language", locales.toLanguageTags())
323+
.build()
324+
return chain.proceed(request)
325+
}
326+
}
327+
```
328+
329+
2. **NetworkServiceLocator** - Configures OkHttp with the interceptor:
330+
```kotlin
331+
object NetworkServiceLocator {
332+
private val localeProvider: AcceptedLanguageInterceptor.LocaleProvider
333+
get() = AcceptedLanguageInterceptor.LocaleProvider {
334+
LocaleManagerCompat.getApplicationLocales(application)
335+
.takeIf { it.size() > 0 }
336+
?: LocaleManagerCompat.getSystemLocales(application)
337+
}
338+
339+
private val okHttpClient: OkHttpClient by lazy {
340+
OkHttpClient.Builder()
341+
.addInterceptor(AcceptedLanguageInterceptor(localeProvider))
342+
.build()
343+
}
344+
}
345+
```
346+
347+
3. **DemoAcceptLanguageHeader** - Composable UI that calls httpbin.org/get:
348+
- Press "GET" to make a request to httpbin.org
349+
- The server echoes back the Accept-Language header
350+
- Shows how different locales result in different Accept-Language values
351+
- Example: English → `"en"`, Vietnamese → `"vi-VN"`
352+
353+
4. **MyApplication** - Initializes the network service locator:
354+
```kotlin
355+
class MyApplication : Application() {
356+
override fun onCreate() {
357+
super.onCreate()
358+
NetworkServiceLocator.init(this)
359+
}
360+
}
361+
```
362+
363+
**Why this matters:**
364+
- Demonstrates real-world usage of locale information in API calls
365+
- Shows proper architecture for locale-aware networking
366+
- Useful pattern for apps that need server-side localization
367+
- The Accept-Language header helps servers return content in the user's preferred language
273368

274369
## Adding New Languages
275370

276371
Adding a new language is straightforward:
277372

278373
**1. Update build configuration** (`app/build.gradle.kts`):
279374
```kotlin
280-
val SUPPORTED_LOCALES = setOf(
281-
"en", "en-rUS",
282-
"vi", "vi-rVN",
283-
"fr", "fr-rFR", // ← Add new locale
284-
)
375+
object Locales {
376+
val localeFilters = listOf(
377+
"en",
378+
"vi-rVN",
379+
"fr-rFR", // ← Add new locale
380+
)
381+
// ...
382+
}
285383
```
286384

287385
**2. Create resource directory** `app/src/main/res/values-{lang}/`

0 commit comments

Comments
 (0)