2026/02/14

오늘의 이야기



아무래도 휴대폰에 앱을 개발하는 일이다 보니, 문자 전송 같은 기본 기능을 이용하는 앱을 개발하는 일이 많기는 하다.  요새는 카카오톡등을 이용하거나, FCM 등을 이용해서 알림을 보내는 기능등을 구현해 보지만, 예전 방식 처럼 SMS 을 전송해 보는 것도 오랜만 이기는 하다.


구글이 계정 정책등으로 인해 앱을 등록할 때 기본앱으로 사용할 수 없는 SMS 앱은 등록에 무리가 있다. 그래서 그냥 개인적으로 사용하기 위해서 기본앱 기능은 아니지만, 필요에 의한 문자 전송을 구현하는 앱을 만들어 볼 까 싶다.


먼저 앱을 구동하기 위해서는 Manifest 파일에 권한 설정을 등록해 준다. 


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.smssend0823">

<uses-permission android:name="android.permission.SEND_SMS" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.SMSSend0823">

......

</application>

</manifest>

SMS 발송 권한을 설정했으니, 앱을 실행하면서 실제 동작이 발생 하기 전에 사용자에게 권한을 획득 하는 코드를 activity 에 넣어 보자.


import android.Manifest;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.telephony.SmsManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.navigation.fragment.NavHostFragment;

import com.example.smssend0823.Database.DBHandler;
import com.example.smssend0823.Database.MsgTargetBean;
import com.example.smssend0823.Database.SendMessageAdapter;
import com.example.smssend0823.Database.SendMessageBean;
import com.example.smssend0823.Utils.OnBackPressedListener;
import com.example.smssend0823.databinding.FragmentMsgselectBinding;

import java.util.ArrayList;

public class MsgSelectFragment extends Fragment implements OnBackPressedListener {

private static final String TAG = "MsgSelectFragment";
private FragmentMsgselectBinding binding;
ArrayList<SendMessageBean> beans;
SendMessageAdapter adapter;
DBHandler dbHandler ;
String phoneNo ;
String message ;
private static final int PERMISSION_RQST_SEND = 101;

public static Fragment newInstance() {
MsgSelectFragment fragment = new MsgSelectFragment();
return fragment;
}

@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState
) {

binding = FragmentMsgselectBinding.inflate(inflater, container, false);
return binding.getRoot();

}

public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);

beans = new ArrayList<>();
doDisplaySendMessage();
binding.listSendMessage.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
int iCnt = 0 ;
for(int i=0; i < beans.size() ; i++) {
if (beans.get(i).isSelectedMsg()) iCnt++;
}
if (iCnt > 0) {
beans.get(position).setSelectedMsg(false);
adapter.notifyDataSetChanged(beans);
Toast.makeText(getContext(), iCnt +"개 이상 선택할 수 없습니다.", Toast.LENGTH_SHORT).show();
return;
}
beans.get(position).setSelectedMsg(!beans.get(position).isSelectedMsg());
adapter.notifyDataSetChanged(beans);
}
});

binding.btnSendMessage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = 0 ; int iCnt = 0 ;
for(int i=0; i < beans.size() ; i++) {
if (beans.get(i).isSelectedMsg()) {
position = i;
iCnt++;
break;
};
}
if (iCnt == 0) {
Toast.makeText(getContext(), "전송할 메시지를 선택하세요.", Toast.LENGTH_SHORT).show();
return;
}
message = beans.get(position).getSendMessage() ;
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("메시지 전송")
.setMessage("[" + beans.get(position).getSendMessage() + "] 로 전송할까요?")
.setPositiveButton("확인", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dbHandler = DBHandler.open(getContext());
Cursor rs = dbHandler.selectAll();
while(rs.moveToNext()) {
MsgTargetBean msgTargetBean = new MsgTargetBean();
msgTargetBean.setId(rs.getInt(rs.getColumnIndex("_id")));
msgTargetBean.setSendSeqNo(rs.getString(rs.getColumnIndex("sendSeqNo")));
msgTargetBean.setReceivePhoneNum(rs.getString(rs.getColumnIndex("receivePhoneNum")));
msgTargetBean.setSendMessage(rs.getString(rs.getColumnIndex("sendMessage")));
msgTargetBean.setSendTy(rs.getString(rs.getColumnIndex("sendTy")));
if("S".equals(msgTargetBean.getSendTy())) {
phoneNo = msgTargetBean.getReceivePhoneNum();
if (sendSMSMessage()) {
msgTargetBean.setSendTy("Y");
msgTargetBean.setSendMessage(message);
dbHandler.update(msgTargetBean);
}
}
}
dbHandler.close();
}
})
.setNegativeButton("취소", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {

}
});
AlertDialog dialog = builder.create();
dialog.show();
}
});
}

private void doDisplaySendMessage() {
beans.clear();
dbHandler = DBHandler.open(getContext());
Cursor rs = dbHandler.selectMsgAll();
while (rs.moveToNext()) {
SendMessageBean bean = new SendMessageBean();
bean.setSendMessage(rs.getString(rs.getColumnIndex("messageString")));
beans.add(bean);
}
adapter = new SendMessageAdapter(beans, getContext());
adapter.notifyDataSetChanged(beans);
binding.listSendMessage.setAdapter(adapter);
}

protected boolean sendSMSMessage() {
if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(getContext(), "SMS 전송 권한에 대한 허가가 필요합니다.", Toast.LENGTH_SHORT).show();
if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(),Manifest.permission.SEND_SMS)) {
Log.e(TAG, "shouldShowRequestPermissionRationale");
}
else { ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.SEND_SMS}, PERMISSION_RQST_SEND);
Log.e(TAG, "requestPermissions");
}
} else {
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(phoneNo, null, message, null, null);
Toast.makeText(getContext(), "메시지가 전송 되었습니다.",Toast.LENGTH_LONG).show();
return true ;
}
return false;
}
//Now once the permission is there or not would be checked
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_RQST_SEND: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

} else {Toast.makeText(getContext(), "SMS 발송이 되지 않았습니다. 잠시 뒤에 다시 시도 하세요.", Toast.LENGTH_LONG).show();
return;
}
}
}
}

@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}

@Override
public void onBackPressed() {
getParentFragmentManager().beginTransaction()
.replace(R.id.container, MsgSendListFragment.newInstance())
.commitNow();
}
}

 


source code 의 예시와 같이 sendSMSMessage() 함수에는 메시지 발송전에 권한 획득 여부를 확인하고, 권한이 없다면 사용자에게 권한 허가를 요청하는 알림을 띄워 권한을 획득 하게 된다.










다만, 이런 기능을 앱으로 이용하려면 playstore 에는 등록에 제한이 있으므로 다른 스토어를 이용하여야 한다.


SMS을 보내는 다른 방법은 권한 없이 사용할 수 있는 SMS Retriever API 을 활용하는 방법이 있다고 할 수 있는데, 실제 앱을 개발하다 보면 불편한 사항이 생기게 되어 있다.


https://developers.google.com/identity/sms-retriever/overview



 


SMS Retriever API를 사용한 자동 SMS 확인  |  SMS Verification APIs


이 페이지는 Cloud Translation API를 통해 번역되었습니다. Switch to English SMS Retriever API를 사용한 자동 SMS 확인 SMS Retriever API를 사용하면 사용자가 인증 코드를 수동으로 입력 할 필요없이 추가 앱 권


developers.google.com




참고해서 보면 좋을 것 깥다.


 















오늘의 이야기



Android 스튜디오: 절전 모드


Android Studio: 절전 모드는 Android 스튜디오 절전 모드입니다. 코드를 연 후 자동으로 프롬프트가 표시되지 않고 어떤 클래스와 메서드가 참조되는지 직관적이지 않으며 코드 자체 검사가 적용되지 않습니다.


인터넷을 찾다가 발견한 설명이다. 




 


이걸 체크 하면 불편한 점...  코드 입력시 auto coding 을 지원 하지 않는다.  이것이 제일 불편하다고 할 수 있을 것 같다.










 























오늘의 이야기



Android 스튜디오: 절전 모드


Android Studio: 절전 모드는 Android 스튜디오 절전 모드입니다. 코드를 연 후 자동으로 프롬프트가 표시되지 않고 어떤 클래스와 메서드가 참조되는지 직관적이지 않으며 코드 자체 검사가 적용되지 않습니다.


인터넷을 찾다가 발견한 설명이다. 




 


이걸 체크 하면 불편한 점...  코드 입력시 auto coding 을 지원 하지 않는다.  이것이 제일 불편하다고 할 수 있을 것 같다.




반응형






 























오늘의 이야기











반응형







앱을 하나 만들다 보니.... 전체 화면에 보일 수 있도록 만들어 달라는 요청이 있네... 어떻게 하나 ?


구글님에게 질문을 해 본다... 그러고선 이렇게 구현을 해 보았다.


    @Override
protected void onPostResume() {
super.onPostResume();

Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
hideSystemUI();
}
}, 500);
}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
hideSystemUI();
}
}

private void hideSystemUI() {

View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);

decorView.requestLayout();
}

 


먼저 activity 의 lifecycle 을 대해서 찾아볼 필요가 있다. 왜 그런가 하면 액티비티가 생성되면서 어느 시점에 화면을 다시 그리게 할 것인가를 정해야 할 것 같아서 이다.  개발자 페이지에서 받아온 이미지 하나를 링크을 달았다.


저 그림의 순서을 보면 onCreate 나 onStart 에 화면 다시 그리기를 넣어 놓으면 처음 실행할 때는 적용이 되겠지만, 다른 액티비티로 갔다가 돌아올때는 실행이 되지 않는 다, 그래서 혹시나, 이 액티비티에서 다른 액티비티를 호출했다가 돌아와야 하는 경우가 있다면 필수적으로 onResume에 넣어 주는 것이 좋을 것 같다.


이미지 출처 : https://developer.android.com/guide/components/activities/activity-lifecycle



 


그리고 위의 소스에서 onWindowFocusChanged 는 구글링에서 나온 부분일껀데, 아마도 윈도우가 다시 활성화 되는 시점에 처리를 하고 넣은 게 아닐까 ? 


뭐 아무튼 이렇게 처리를 하게 되면 앱이 실행되고 나서 화면 전체를 차지하게 된다.


내가 만든 앱 실행화면



이런식으로 위/아래 있는 상태바, 네비게이션바등이 다 숨어지게 된다.


 


끝.





반응형























오늘의 이야기



#스치니1000프로젝트 #재미 #행운기원 #Compose #Firebase

🎯 야 너 토요일마다 로또 확인하냐?
나도 맨날 "혹시나~" 하면서 봤거든 ㅋㅋ

근데 이제는 그냥 안 해
AI한테 맡겼어 🤖✨

그것도 구글 Gemini로다가!

그래서 앱 하나 만들었지
👉 "로또 예상번호 by Gemini" 🎱

AI가 분석해서 번호 딱! 뽑아줌
그냥 보고 참고만 하면 됨

재미로 해도 좋고…
혹시 모르는 거잖아? 😏


https://play.google.com/store/apps/details?id=com.billcorea.gptlotto1127




오늘의 이야기





반응형







 




집에 오는 길에... 길가에 있던 들꽃 


이름은 무엇인가 ?


날이 더운데 잘 지내는 지 ?


오늘 하루도 좋은 하루가 되었길 바라며...


다음에 다시 만나게 되면 더 좋은 이야길 할 수 있게 되길 바라며...





반응형





























오늘의 이야기





반응형







앱을 만들다가... 갤러리에서 이미지 받아오는 와서 사용하는 것을 구현하고 있는 중인데...


사진이 돌아간다. 흑~ 그래서 구글링 신에서 질문을 했다...


답... Exif 을 구현해서 사진을 돌리는 코드가 나오고... 


그것 보다는 조금 간결한 코드 구현 방법이 보인다.


http://bumptech.github.io/glide/doc/getting-started.html



 


Glide v4 : Getting Started


Basic Usage Loading images with Glide is easy and in many cases requires only a single line: Glide.with(fragment) .load(myUrl) .into(imageView); Cancelling loads you no longer need is simple too: Glide.with(fragment).clear(imageView); Although it's good


bumptech.github.io




glide 이건 뭔가 ???  쉬운 이미지 처리 방법 


 


백그라운드 스레드
배경 스레드에 이미지를 로드하는 것도 submit(int, int)다음을 사용하여 간단합니다 .

FutureTarget<Bitmap> futureTarget =
Glide.with(context)
.asBitmap()
.load(url)
.submit(width, height);

Bitmap bitmap = futureTarget.get();

이렇게 설명된 부분을 참조 하면 될 것 같다. 또 하나 방법은 바로 imageView 에 넣는 방법인데...  


알고 싶은 건 Bitmap 에 값을 넣는 것이니... 그런데... 백그라운드 스레드로 해야 한다는 부분을 주의 해야 했다.


그래서 아래 처럼 코딩해서 해소 끝...


            FutureTarget<Bitmap> futureTarget =
Glide.with(getApplicationContext())
.asBitmap()
.load(photoUri)
.submit(width, height);

Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {

try {
Bitmap bitmap = futureTarget.get();

GroupImageBean bean1 = new GroupImageBean();
bean1.setImageGrpId(groupName);
bean1.setImageData(bitmap);
bean1.setOriginalUri(photoUri);
bean1.setImageUri(photoUri);
bitmapArrayList.add(bean1);

dbHandler = DBHandler.open(SetGroupActivity.this);
dbHandler.insertImage(bean1);
dbHandler.close();

getAdapterUpdateReceiptsList();
} catch (Exception e) {
e.printStackTrace();
}

}
}, 660);

 


다음에 또 이런걸 하게 될 지 모르지만... 


 


아래 코드는 갤러리에서 사진 선택할 수 있도록 호출 하기 위한 코드 ...


 


                Intent intent = new Intent();
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) // 이건 동시에 여러장 선택
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(intent, REQUEST_CODE);

저렇게 호출하면 그걸 받아서 처리하는 부분은 


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
if(requestCode == REQUEST_CODE)
{
if(resultCode == RESULT_OK)
{
try{
InputStream in = getContentResolver().openInputStream(data.getData());

Bitmap img = BitmapFactory.decodeStream(in);
in.close();

imageView.setImageBitmap(img);
}catch(Exception e)
{

}
}
else if(resultCode == RESULT_CANCELED)
{
Toast.makeText(this, "사진 선택 취소", Toast.LENGTH_LONG).show();
}
}
}

이렇게 activity 호출 결과를 받아서 처리하면 됨...


오늘 도 즐~~~ 코딩...





반응형





























오늘의 이야기


#스하리1000명프로젝트

스치니들!
내가 만든 이 앱은, 내 폰에 오는 알림 중에서 중요한 키워드가 있는 경우
등록해둔 친구에게 자동으로 전달해주는 앱이야 📲

예를 들어, 카드 결제 알림을 와이프나 자녀에게 보내주거나
이번 달 지출을 달력처럼 확인할 수도 있어!

앱을 함께 쓰려면 친구도 설치 & 로그인해줘야 해.
그래야 친구 목록에서 서로 선택할 수 있으니까~
서로 써보고 불편한 점 있으면 알려줘 🙏

👉 https://play.google.com/store/apps/details?id=com.nari.notify2kakao





오늘의 이야기





반응형







앱을 하나 만들고 있는 중인데, 


하다 보니  중복이 없는 random 값을 추출해야 하는 고민에 빠짐... 


와이프님의 백신 예약도 해야 하는 날인데,  예약 서버는 버벅 거리고... 나의 머리도 버벅 거리고...


우째 우쨰 드뎌 완성!!!


아래 소스는 간단히 설명 하자면... 사용하기 위해서는 우선 


import java.util.Random


이 필요하다...  그리고 아래 나열된 부분은 함수 부분만 적었으니, 어딘가에서 호출하는 부분도 있어야 할 것 같다.


    @Override
protected void onCreate(Bundle savedInstanceState) {

...
int[] dispOrder = getRandomCount(iTyCnt);
...

}

뭐 이런식으로 다가...  getRandomCount 로 넘어가는 파라미터인 count 는 추출해야 하는 숫자의 제일 큰 값 이라고 


보면 된다.  물론 시작하는 숫자는 0 이고. 그래서 0 ~ count 범위 내에서 random 한 숫자를 만들어서 


결과로 돌려 주는 것이다.


    /**
* 중복이 없는 랜덤한 숫자 만들기
* @param count
* @return
*/
public int[] getRandomCount(int count) {
int[] nResult = new int[count];

for(int i=0; i < count; i++) {
if (i == 0) {
nResult[i] = getRandomValue(count) ;
} else {
nResult[i] = getRandom(i, count, nResult); // 배열 사용을 위해서 -1 했음.
}
Log.e(TAG, "------[" + i + "]=" + nResult[i]) ;
}

return nResult ;
}

/**
* 재귀호출을 통해서 중복이 없는 숫자를 만든다.
* @param ii : 선택된 위치
* @param count : 제일 큰 값의 범위
* @param nResult : 결과를 저장하는 배열
* @return
*/
private int getRandom(int ii, int count, int[] nResult) {

int randomValue = getRandomValue(count) ;
for(int i = 0 ; i < ii ; i++) {
//Log.e(TAG, "nnn [" + i + "]=" + "<" + ii + ">" + nResult[i] + "==" + randomValue) ;
if (randomValue == nResult[i]) {
randomValue = getRandom(ii, count, nResult) ;
break;
}
}
return randomValue ;
}

/**
* 랜덤하게 숫자를 하나 만듬
* @param count : 제일 큰 값의 범위
* @return
*/
private int getRandomValue(int count) {
Random random = new Random();
return random.nextInt(count);
}

 


궁금한 부분이 있다면  아래 댓글로 달아 두시면 답을 적어 드립니다.


 





반응형





























오늘의 이야기











반응형







앱을 만들어 등록하는 동안 하고 싶은 것 중에서 한가지는 여러나라의 언어로 배포를 해 보는 것이였다. 예전에는 그걸 하나 하나 번역을 해 가면서 등록을 했던 것을 카카오 번역 API 호출하여 사용하는 방법으로 해소해 보자.


일단, 목적으로 하는 앱을 만드는 과정을 했다고 치고. 기본 언어를 영어(아니면 한국어도 상관 없이)로 하고 앱을 만든다음 android studio 에서 번역을 하기 위한 준비를 해 보자.




언어 번역을 위해서 Translations Editor 을 열어 아이콘 중에서 지구모양 아이콘을 클릭 하면 어떤 언어를 선택할 것이지 정할 수 있다.  그것중에서 일단, 영어, 한국어, 일본어, 베트남어 (2022년 월드컵 예선 진출을 응원하며 ...) 선택해서 만들어 보기로 했다.




영어 버전을 예시로 하기 위해서 샘플은 영어 버전 string.xml 을 표시해 본다.


 


 


 


 


이제 번역은 어떻게 할 것인가 ?


카카오 개발자 페이지 (https://developers.kakao.com/console/app) 에서 새 어플리케이션을 등록하고




 




팝업창에서 앱이름, 사업자명 (없으면 우리집 이렇게라도)


입력하고 저장하기를 하면 되고


 


 


 




 


앱키를 취득하면 준비는 일단 완료된 것이다


이중에서 우리는 REST API 을 이용할 것이기 떄문에


REST API 키를 기억하고 있으면 된다.


 


 


 


다음은 python 을 이용할 것인데, python 의 설치 등등에 관한 것은 구글링을 통해서 많이 볼 수 있으므로 여기서는 생략해 본다.


참고로 python 을 작업하는 것도 pycham 이라는 툴을 사용하고 있는데, 이것에 대한 설명도 생략하므로 참고하시길


import requests

# 카카오 API 번역 호출
def translate(StringValue):
url = "https://dapi.kakao.com//v2/translation/translate"
headers={ "Authorization" : "KakaoAK f57a4f***********5006481"}
data = {"query" : StringValue, "src_lang" : "en", "target_lang" : "jp"}

response = requests.get(url, headers=headers, params=data)

if response.status_code == 200:
return response.json()["translated_text"]

# android string.xml 을 이용한 번역하기
def getString(lines):
line = lines.split(">")
try:
s1 = translate(line[1].split("<")[0])
s = str(line[0]) + ">" + str(s1[0][0]) + "<" + str(line[1].split("<")[1]) + ">"
return s
except:
return lines

# 내앱의 영문 기준 스트링 파일
f = open("C:/workspaces/Bo****26/app/src/main/res/values/strings.xml", 'r')
while True:
line = f.readline()
if not line: break
if 'translatable' in line:
pass
else:
print(getString(line))

f.close()

먼저 python 에서 기동할 소스인데,  주의할 것은 headers 에 있는 API KEY 


headers={ "Authorization" : "KakaoAK f57a4f***********5006481"} 에서 f57a... 부분은 여러분이 받은 키값으로 변경해야 하며 


아래 부분에 open 에서 사용하는 파일도 여러분이 작성한 strings.xml 파일의 경로를 지정해 주어야 한다. 이 예시는 영문을 기준으로 변환을 하는 것이라서 저 소스상에 있는 src_lang 가 en 으로 되어 있으니 한국어를 기준으로 하려면 kr 로 변경해야 한다. 


카카오에서 지원해주는 번역에서 언어표기는 아래 표시된 정도이니 참고해서 사용하면 된다. src_lang 에는 원본 target_lang 에는 번역하고 싶은 언어 표기로 설정해 주면 된다. (https://developers.kakao.com/docs/latest/ko/translate/common)

















































































한국어kr
영어en
일본어jp
중국어cn
베트남어vi
인도네시아어id
아랍어ar
뱅갈어bn
독일어de
스페인어es
프랑스어fr
힌디어hi
이탈리아어it
말레이시아어ms
네덜란드어nl
포르투갈어pt
러시아어ru
태국어th
터키어tr

 


이제 실행을 해보면 아래 예시 그림과 같이 번역이 되어 나오는 것을 볼 수 있다.




이 내용을 그대로 android studio 에서 생성된 일본어 strings.xml 에 붙여 넣으면 번역 끝.


다만, 직역을 하기 때문에 그 나라에 사시는 분들이 이해를 하는 데, 도움이 될까는 조금 고민해 볼 필요가 있을 것 같기는 하다.


이것으로 다국적(?)앱 만들어 끝!!!





반응형























오늘의 이야기


#스하리1000명프로젝트,
Иногда сложно разговаривать с иностранными работниками, правда?
Я сделал простое приложение, которое помогает! Вы пишете на своем языке, а другие видят это на своем.
Он автоматически переводит в зависимости от настроек.
Очень удобно для легкого общения. Посмотрите, когда будет возможность!
https://play.google.com/store/apps/details?id=com.billcoreatech.multichat416




오늘의 이야기

아무래도 휴대폰에 앱을 개발하는 일이다 보니, 문자 전송 같은 기본 기능을 이용하는 앱을 개발하는 일이 많기는 하다.  요새는 카카오톡등을 이용하거나, FCM 등을 이용해서 알림을 보내는 기능등을 구현해 보지만, 예전 방식 처럼 SMS 을 전송해 보는...